package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {
    private ServerSocket serverSocket = null;//在外面揽客的socket
    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        //线程池
        ExecutorService pool = Executors.newCachedThreadPool();
        while(true) {

//            while (true) {
//                //通过accept方法来接听电话,然后才能通信
                Socket clientSocket = serverSocket.accept();//职业顾问小姐姐,负责后续的通信,每个客户端都会分配一个职业顾问
//                //主线程负责accept
//                Thread t = new Thread(() ->{
//                    processConnection(clientSocket);//其他线程实现对客户端的服务作用
//                });
//                t.start();
//            }
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    processConnection(clientSocket);
                    //线程池是解决线程频繁创建销毁的问题
                }
            });
        }
    }
    //通过这个方法来处理一次连接,连接建立的过程中会涉及到多次的请求响应交互
    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s:%d] 客户端上线!\n ",clientSocket.getInetAddress(),clientSocket.getPort());//tcp是有连接的会保存客户端的ip端口
        //循环读取客户端请请求并返回响应
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) {//inputStream:往网卡读数据,outputStream:往网卡写数据
            //tcp是面向字节流的,使用和文件操作一样的类和方法完成对tcp socket的读写
            while (true) {
                //通过 inputStream 读取数据
                Scanner scanner = new Scanner(inputStream);//把输入流包装,方便进一步的读取
                if(!scanner.hasNext()) {
                    //读取完毕,客户端断开连接,就会产生读取完毕
                    System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),clientSocket.getPort());

                    break;
                }
                //1.读取请求并解析,注意 next读的时候要毒打空白符才结束
                //因此要求客户端发送的请求必须带有空白符结尾.如\n 或者空格
                String request = scanner.next();

                //2.根据请求计算响应
                String response = process(request);

                //3.把响应返回给客户端
//                outputStream.write(response.getBytes(),0,response.getBytes().length);这种方式不方便给返回的响应中添加\n
                //也可以给outPutStream套上一层,更方便的完成写入
                PrintWriter printWriter = new PrintWriter(outputStream);//把输出流包装,方便进一步的写入
                printWriter.println(response);//在response加入换行这样就符合格式
                //通过这个flush()注定刷新缓冲区,确保数据真的发送出去了
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s, resp: %s]\n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);//这里要注意%s的数量和放进去的东西的数量要一样
                //把字节流转换成字符流


            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                clientSocket.close();//需要针对clientSocket进行close操作,释放文件描述符表
                //severSocket 整个程序只有唯一一个对象,并且整个对象的生命周期很长是要跟随整个程序的,整个对象无法提前关闭,只要程序退出
                //跟随进程一起被销毁释放即可(不需要手动进行)

            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }
}


